package com.us.usws.support.uitls;

import java.net.MalformedURLException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import net.iwsh.core.common.GlobalConfig;
import net.iwsh.core.exception.GenericRuntimeException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.caucho.hessian.client.HessianProxyFactory;
import com.us.usws.common.SiteConstants;
import com.us.usws.support.ext.GuiceInjectorFactory;

/**
 * Service Factory
 */
public class ServiceFactory {

	private static Log log = LogFactory.getFactory().getInstance(ServiceFactory.class);
	private static ServiceFactory instance = new ServiceFactory();

	//class cache
	private Map<String, Class<?>> clzCache = Collections.synchronizedMap(new HashMap<String, Class<?>>());

	private ServiceFactory() {
	}

	/**
	 * get instance
	 * 
	 * @return
	 */
	public static ServiceFactory getInstance() {
		return instance;
	}

	/**
	 * get service
	 * @param iClz
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> T getService(Class<T> iClz) {
		//check whether impl class in cache
		String fullName = iClz.getName();
		if (clzCache.containsKey(fullName)) {
			log.trace("Found cached class impl for [" + fullName + "].");
			return getService(iClz, clzCache.get(fullName));
		}

		//not found in cache
		synchronized (clzCache) {
			//found impl classes
			String clzName = iClz.getSimpleName();
			String pkg = iClz.getPackage().getName();
			String[] newClzNames = new String[] { pkg + ".impl." + clzName + "Impl", pkg + "." + clzName + "Impl" };
			Class<T> newClz = null;
			for (String each : newClzNames) {
				try {
					newClz = (Class<T>) Class.forName(each);
					clzCache.put(fullName, newClz);
					return getService(iClz, newClz);
				} catch (ClassNotFoundException e) {
					//not found
					log.debug("Cannot found Impl class of [" + each + "]");
				}
			}
		}
		log.error("Impl class not found -- [" + iClz.getName() + "].");
		return null;
	}

	/**
	 * get user service
	 * @return
	 */
	private <T> T getService(Class<T> iClz, Class<?> implClz) {
		String clzName = iClz.getSimpleName();
		return createInstance("WebServiceClient/" + clzName, iClz, implClz);
	}

	/**
	 * Genernal method of creating instance
	 * 
	 * @param <T>
	 * @param type
	 * @param interfaceClass
	 * @param implClass
	 */
	@SuppressWarnings("unchecked")
	private <T> T createInstance(String serviceConfPath, Class<T> interfaceClass, Class<?> implClass) {
		String type = GlobalConfig.getString("ServiceFactory/type", "DIRECT");
		String serviceURL = GlobalConfig.getString(serviceConfPath);

		if (SiteConstants.SERVICE_FACTORY_TYPE_WSDL.equals(type)) {
			// WSDL web service
			log.warn("Not implements WSDL yet.");

		} else if (SiteConstants.SERVICE_FACTORY_TYPE_DIRECT.equals(type)) {
			// direct new instance
			try {
				return (T) implClass.newInstance();
			} catch (Exception e) {
				log.error("Cannot create new instance of " + implClass, e);
			}

		} else if (SiteConstants.SERVICE_FACTORY_TYPE_GUICE.equals(type)) {
			//guice
			return GuiceInjectorFactory.getBinded(interfaceClass);

		} else if (SiteConstants.SERVICE_FACTORY_TYPE_HESSIAN.equals(type)) {
			// hessian
			HessianProxyFactory factory = new HessianProxyFactory();
			try {
				return (T) factory.create(interfaceClass, serviceURL);
			} catch (MalformedURLException e) {
				log.error("Cannot create hessian client of [" + interfaceClass.getName() + "] with [" + serviceURL + "].", e);
			}

		} else if (SiteConstants.SERVICE_FACTORY_TYPE_REST.equals(type)) {
			// REST
			log.warn("Not implements REST yet.");
		}
		throw new GenericRuntimeException("Cannot create service for type: " + type);
	}
}
